package com.andnnl.event.dispatch;

import com.andnnl.event.events.Event;

import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

/**
 * 事件分发
 * Created by chenss on 2021/5/11.
 */
public class EventDispatcher {
    private final BlockingQueue<Event> eventQueue;
    private final ExecutorService eventHandlerPool;
    protected final Map<EventType, Class<? extends EventHandler>> eventDispatchers = new ConcurrentHashMap<>();
    private Thread eventHandlingThread;

    private volatile boolean stopped = false;

    public EventDispatcher(BlockingQueue<Event> eventQueue, ExecutorService eventHandlerPool) {
        this.eventQueue = eventQueue;
        this.eventHandlerPool = eventHandlerPool;
        System.out.println("Event dispatcher starting...");
    }

    Runnable createThread() {
        return new Runnable() {
            @Override
            public void run() {
                while (!stopped && !Thread.currentThread().isInterrupted()) {
                    Event event;
                    try {
                        event = eventQueue.take();
                    } catch (InterruptedException ie) {
                        if (!stopped) {
                            System.out.println("Dispatcher thread interrupted");
                            ie.printStackTrace();
                        }
                        return;
                    }
                    if (event != null) {
                        dispatch(event);
                    }
                }
            }
        };
    }

    @SuppressWarnings("unchecked")
    protected void dispatch(Event event) {
        EventType type = event.getEventType();
        try {
            Class<? extends EventHandler> handlerClazz = eventDispatchers
                    .get(type);
            if (handlerClazz != null) {
                EventHandler handler = handlerClazz.newInstance();
                handler.setEvent(event);
                eventHandlerPool.submit(handler);
            } else {
                throw new Exception("No handler for registered for " + type);
            }
        } catch (Throwable t) {
            System.err.println("Error in dispatcher thread");
            t.printStackTrace();
            System.exit(-1);
        }
    }

    public void register(EventType eventType,
                         Class<? extends EventHandler> handler) {
        Class<? extends EventHandler> registeredHandler = eventDispatchers
                .get(eventType);
        System.out.println("Registering " + eventType + " for "
                + handler);
        if (registeredHandler == null) {
            eventDispatchers.put(eventType, handler);
        }
    }

    public void start() {
        eventHandlingThread = new Thread(createThread());
        eventHandlingThread.setName("AsyncDispatcher event handler");
        eventHandlingThread.start();
        System.out.println("Event dispatcher started!");
    }

    public void stop() {
        stopped = true;
        if (eventHandlingThread != null) {
            eventHandlingThread.interrupt();
            try {
                eventHandlingThread.join();
            } catch (InterruptedException ie) {
                System.out.println("Interrupted Exception while stopping");
                ie.printStackTrace();
            }
        }
    }
}
